package com.larly.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.larly.common.IdRequest;
import com.larly.exception.BusinessException;
import com.larly.exception.ErrorCode;
import com.larly.exception.ThrowUtils;
import com.larly.model.domain.InteractionLike;
import com.larly.model.domain.Interactions;
import com.larly.model.domain.Notes;
import com.larly.model.domain.User;
import com.larly.model.dto.Interactions.AddInteractions;
import com.larly.model.dto.Interactions.EditInteractionsRequest;
import com.larly.model.enums.interactions.InteractionsIsFeaturedEnum;
import com.larly.model.vo.InteractionsVO;
import com.larly.model.vo.UserVO;
import com.larly.service.*;
import com.larly.mapper.InteractionsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author 许颢达
* @description 针对表【interactions(存储所有针对笔记的互动内容，note_id用于关联笔记但无强制外键约束)】的数据库操作Service实现
* @createDate 2025-08-20 14:29:44
*/
@Service
@Slf4j
public class InteractionsServiceImpl extends ServiceImpl<InteractionsMapper, Interactions>
    implements InteractionsService{

    @Resource
    private UserService userService;

    @Resource
    private NotesService notesService;

    @Resource
    private InteractionLikeService interactionLikeService;

    @Resource
    private NotificationService notificationService;

    /**
     * 添加互动内容（适配新表结构）
     * 支持主互动发布和对已有互动的回复
     *
     * @param addInteractions 互动请求参数
     * @param request 请求上下文（获取当前登录用户）
     * @return 新增互动的ID
     */
    @Override
    public Integer addInteractions(AddInteractions addInteractions, HttpServletRequest request){
        // 1. 基础参数校验
        ThrowUtils.throwIf(addInteractions == null, ErrorCode.PARAM_ERROR, "请求参数不能为空");
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN, "用户未登录");

        // 2. 核心字段校验
        Integer noteId = addInteractions.getNoteId();
        ThrowUtils.throwIf(noteId == null, ErrorCode.PARAM_ERROR, "笔记ID不能为空");

        String content = addInteractions.getContent();
        ThrowUtils.throwIf(StringUtils.isBlank(content), ErrorCode.PARAM_ERROR, "互动内容不能为空");
        ThrowUtils.throwIf(content.length() > 2000, ErrorCode.PARAM_ERROR, "互动内容不能超过2000字符");

        // 3. 校验目标笔记是否存在且有效
        Notes targetNote = notesService.getById(noteId);
        ThrowUtils.throwIf(targetNote == null, ErrorCode.NOTE_NOT_FOUND, "目标笔记不存在");
        ThrowUtils.throwIf(targetNote.getIsDeleted() == 1, ErrorCode.OPERATION_DENIED, "目标笔记已删除，无法互动");

        // 4. 处理父级互动（回复场景）
        Integer parentId = addInteractions.getParentId();
        Interactions parentInteraction = null;
        // 若存在父级ID，校验父级互动是否有效
        if (parentId != null && parentId != 0) {
            parentInteraction = this.getById(parentId);
            ThrowUtils.throwIf(parentInteraction == null, ErrorCode.PARAM_ERROR, "父级互动不存在");
            // 校验父级互动是否属于当前笔记（防止跨笔记回复）
            ThrowUtils.throwIf(!parentInteraction.getNoteId().equals(noteId),
                    ErrorCode.PARAM_ERROR, "不能回复其他笔记的互动");
        }

        // 5. 构建互动实体
        Interactions interaction = new Interactions();
        interaction.setNoteId(noteId);
        interaction.setUserId(loginUser.getId());
        interaction.setContent(content);
        // 父级ID默认为0（主互动），允许回复场景传递有效ID
        interaction.setParentId(ObjUtil.defaultIfNull(parentId, 0));
        interaction.setIsFeatured(InteractionsIsFeaturedEnum.NORMAL.getKey()); // 默认非精选
        // 初始化统计字段
        interaction.setLikeCount(0);
        interaction.setReplyCount(0);
        // 依赖数据库默认值，但显式设置更清晰
        interaction.setCreatedAt(new Date());

        // 6. 保存互动记录
        boolean saveResult = this.save(interaction);
        ThrowUtils.throwIf(!saveResult, ErrorCode.OPERATION_DENIED, "添加互动失败");

        // 更新笔记互动通知
        if(targetNote != null){
            notificationService.triggerNoteCommentNotify(noteId, targetNote.getTitle(), content, loginUser.getId(), targetNote.getUserId());
            log.info("用户[{}]触发笔记评论通知，笔记ID:{}，互动ID:{}", loginUser.getId(), noteId, interaction.getId());
        }

        // 7. 若为回复，更新父级互动的回复数
        if (parentId != null && parentId != 0) {
            boolean updateResult = this.lambdaUpdate()
                    .setSql("reply_count = reply_count + 1")
                    .eq(Interactions::getId, parentId)
                    .update();
            // 回复数更新失败不影响主互动创建，但需记录日志
            if (!updateResult) {
                log.warn("互动[{}]的回复数更新失败，父级互动ID:{}", interaction.getId(), parentId);
            }
            // 更新回复互动通知
            if (parentInteraction != null) {
                notificationService.triggerCommentReplyNotify(parentInteraction.getId(),
                        parentInteraction.getContent(), loginUser.getId(), parentInteraction.getUserId());
                log.info("用户[{}]触发回复互动通知，父级互动ID:{}，子级互动ID:{}",
                        loginUser.getId(), parentId, interaction.getId());
            }
        }

        log.info("用户[{}]添加互动成功，互动ID:{}，笔记ID:{}，父级ID:{}",
                loginUser.getId(), interaction.getId(), noteId, parentId);
        return interaction.getId();
    }

    /**
     * 获取笔记的互动列表
     *
     * @param idRequest 笔记ID
     * @return 笔记的互动列表
     */
    @Override
    public List<InteractionsVO> getInteractionsByNoteId(IdRequest idRequest) {
        Integer noteId = idRequest.getId();
        ThrowUtils.throwIf(noteId == null || noteId <= 0, ErrorCode.PARAM_ERROR);
        Notes notes = notesService.getById(noteId);
        ThrowUtils.throwIf(notes == null, ErrorCode.PARAM_ERROR, "笔记不存在");
        List<Interactions> list = this.lambdaQuery().eq(Interactions::getNoteId, noteId)
                .orderByDesc(Interactions::getCreatedAt).list();
        List<InteractionsVO> interactionsVOS = this.listToVoList(list);

        // 获取笔记的互动列表
        return interactionsVOS;
    }

    /**
     * 编辑互动
     *
     * @param editInteractionsRequest
     * @param request
     * @return
     */
    @Override
    public boolean editInteractions(EditInteractionsRequest editInteractionsRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(ObjUtil.isNull(editInteractionsRequest), ErrorCode.PARAM_ERROR);
        Integer interactionsRequestId = editInteractionsRequest.getId();
        ThrowUtils.throwIf(ObjUtil.isNull(interactionsRequestId) || interactionsRequestId <= 0, ErrorCode.PARAM_ERROR, "互动ID不能为空");

        // 获取noteId，如果不为空，校验权限，只能设置互动的精选
        int noteId = editInteractionsRequest.getNoteId();
        User loginUser = userService.getLoginUser(request);
        if(noteId != 0) {
            Notes notes = notesService.getById(noteId);
            ThrowUtils.throwIf(notes == null, ErrorCode.NOTE_NOT_FOUND, "笔记不存在");
            // 校验互动是否是当前笔记下的，防止篡改
            Interactions targetInteraction = this.getById(interactionsRequestId);
            ThrowUtils.throwIf(targetInteraction == null, ErrorCode.RESOURCE_NOT_FOUND, "互动不存在");
            ThrowUtils.throwIf(!targetInteraction.getNoteId().equals(noteId), ErrorCode.OPERATION_DENIED, "互动不属于该笔记，无法操作");
            // 校验精选状态
            Integer isFeatured = editInteractionsRequest.getIsFeatured();
            String value = InteractionsIsFeaturedEnum.getValue(isFeatured);
            ThrowUtils.throwIf(value == null, ErrorCode.PARAM_ERROR, "精选状态无效");
            // 校验权限，只能设置互动的精选
            ThrowUtils.throwIf(!loginUser.getId().equals(notes.getUserId()), ErrorCode.OPERATION_DENIED, "无权限修改");
            // 修改互动
            boolean update = this.lambdaUpdate()
                    .eq(Interactions::getId, interactionsRequestId)
                    .set(Interactions::getIsFeatured, isFeatured)
                    .update();
            ThrowUtils.throwIf(!update, ErrorCode.OPERATION_DENIED, "修改互动失败");
        }else{
            // 如果为空，校验权限，互动者只能修改自己的评论
            Interactions interactions = this.getById(interactionsRequestId);
            ThrowUtils.throwIf(ObjUtil.isNull( interactions), ErrorCode.RESOURCE_NOT_FOUND, "互动数据不存在");
            ThrowUtils.throwIf(!loginUser.getId().equals(interactions.getUserId()), ErrorCode.OPERATION_DENIED, "无操作权限");
            boolean update = this.lambdaUpdate()
                    .eq(Interactions::getId, interactionsRequestId)
                    .set(Interactions::getContent, editInteractionsRequest.getContent())
                    .update();
            ThrowUtils.throwIf(!update, ErrorCode.OPERATION_DENIED, "修改互动失败");
        }
        return true;
    }

    /**
     * 给互动点赞（高并发优化版）
     * 解决幂等性和并发更新问题
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean likeInteractions(IdRequest idRequest, HttpServletRequest request) {
        // 1. 参数校验
        ThrowUtils.throwIf(ObjUtil.isNull(idRequest), ErrorCode.PARAM_ERROR, "参数不能为空");
        Integer interactionId = idRequest.getId();
        ThrowUtils.throwIf(interactionId == null || interactionId <= 0, ErrorCode.PARAM_ERROR, "互动ID无效");

        // 2. 获取登录用户和互动信息
        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();
        Interactions interaction = this.getById(interactionId);
        ThrowUtils.throwIf(ObjUtil.isNull(interaction), ErrorCode.RESOURCE_NOT_FOUND, "互动数据不存在");

        // 3. 幂等性校验：检查是否已点赞（减少无效操作）
        QueryWrapper<InteractionLike> likeQuery = new QueryWrapper<>();
        likeQuery.eq("interaction_id", interactionId).eq("user_id", userId);
        InteractionLike existingLike = interactionLikeService.getOne(likeQuery);
        if (ObjUtil.isNotNull(existingLike)) {
            log.warn("用户[{}]已点赞互动[{}]，无需重复操作", userId, interactionId);
            return false; // 已点赞，返回false
        }

        try {
            // 4. 乐观锁更新点赞数（解决并发更新问题）
            // 条件：where id = ? and version = ?（需在Interactions实体添加version字段并配置乐观锁插件）
            boolean update = this.lambdaUpdate()
                    .eq(Interactions::getId, interactionId)
                    .eq(Interactions::getVersion, interaction.getVersion())
                    .set(Interactions::getLikeCount, interaction.getLikeCount() + 1)// 点赞数加1
                    .set(Interactions::getVersion, interaction.getVersion() + 1)// 版本号加1
                    .update();

            // 5. 检查乐观锁更新结果（若失败，说明并发更新冲突）
            ThrowUtils.throwIf(!update, ErrorCode.OPERATION_DENIED, "点赞失败，请重试");
            log.debug("互动[{}]点赞数更新成功，新点赞数：{}", interactionId, interaction.getLikeCount() + 1);

            // 6. 记录点赞关系
            InteractionLike interactionLike = new InteractionLike();
            interactionLike.setInteractionId(interactionId);
            interactionLike.setUserId(userId);
            boolean saveResult = interactionLikeService.save(interactionLike);
            ThrowUtils.throwIf(!saveResult, ErrorCode.OPERATION_DENIED, "保存点赞记录失败");

            log.info("用户[{}]点赞互动[{}]成功", userId, interactionId);

            // 添加互动点赞通知
            if (interaction != null){
                notificationService.triggerCommentLikeNotify(interactionId, interaction.getContent(), userId, interaction.getUserId());
                log.info("用户[{}]点赞互动[{}]成功，添加点赞通知成功", userId, interactionId);
            }
            return true;

        } catch (Exception e) {
            // 7. 处理并发插入导致的唯一索引冲突（uk_interaction_user）
            if (e instanceof SQLIntegrityConstraintViolationException || e.getMessage().contains("uk_interaction_user")) {
                log.warn("用户[{}]并发点赞互动[{}]，唯一索引拦截重复操作", userId, interactionId);
                return false; // 视为已点赞
            }
            // 其他异常触发事务回滚
            log.error("用户[{}]点赞互动[{}]失败", userId, interactionId, e);
            throw new BusinessException(ErrorCode.FAIL);
        }
    }

    /**
     * 取消互动点赞（高并发优化版）
     * 解决并发更新点赞数和逻辑严谨性问题
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelLikeInteractions(IdRequest idRequest, HttpServletRequest request) {
        // 1. 参数校验
        ThrowUtils.throwIf(ObjUtil.isNull(idRequest), ErrorCode.PARAM_ERROR, "参数不能为空");
        Integer interactionId = idRequest.getId();
        ThrowUtils.throwIf(interactionId == null || interactionId <= 0, ErrorCode.PARAM_ERROR, "互动ID无效");

        // 2. 获取登录用户和互动信息
        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();
        Interactions interaction = this.getById(interactionId);
        ThrowUtils.throwIf(ObjUtil.isNull(interaction), ErrorCode.RESOURCE_NOT_FOUND, "互动数据不存在");

        // 3. 检查是否存在点赞记录（幂等性处理）
        QueryWrapper<InteractionLike> likeQuery = new QueryWrapper<>();
        likeQuery.eq("interaction_id", interactionId).eq("user_id", userId);
        InteractionLike existingLike = interactionLikeService.getOne(likeQuery);
        if (ObjUtil.isNull(existingLike)) {
            log.warn("用户[{}]未点赞互动[{}]，取消点赞操作忽略", userId, interactionId);
            return false; // 未点赞，无需操作
        }

        try {
            // 4. 乐观锁更新点赞数（解决并发问题）
            // 确保点赞数不会小于0（防止异常场景下的负数）
            int newLikeCount = Math.max(interaction.getLikeCount() - 1, 0);
            boolean update = this.lambdaUpdate()
                    .set(Interactions::getLikeCount, newLikeCount)
                    .set(Interactions::getVersion, interaction.getVersion() + 1) // 版本号+1
                    .eq(Interactions::getId, interactionId)
                    .eq(Interactions::getVersion, interaction.getVersion()) // 乐观锁条件
                    .update();

            // 5. 检查乐观锁更新结果（并发冲突处理）
            ThrowUtils.throwIf(!update, ErrorCode.OPERATION_DENIED, "取消赞失败，请重试");

            // 6. 删除点赞记录
            boolean removeResult = interactionLikeService.remove(likeQuery);
            ThrowUtils.throwIf(!removeResult, ErrorCode.OPERATION_DENIED, "删除点赞记录失败");

            log.info("用户[{}]取消点赞互动[{}]成功，新点赞数：{}", userId, interactionId, newLikeCount);
            return true;

        } catch (Exception e) {
            log.error("用户[{}]取消点赞互动[{}]失败", userId, interactionId, e);
            throw e; // 触发事务回滚
        }
    }



    // --------------------------工具-----------------------------
    /**
     * 将互动列表转换为带层级结构的Vo列表
     * 主评论（parentId=0）包含子回复列表（children）
     */
    public List<InteractionsVO> listToVoList(List<Interactions> interactionsList) {
        if (CollectionUtil.isEmpty(interactionsList)) {
            return new ArrayList<>();
        }

        // 1. 批量获取所有涉及的用户信息（避免N+1查询）
        Set<Integer> userIds = interactionsList.stream()
                .map(Interactions::getUserId)
                .collect(Collectors.toSet());
        Map<Integer, UserVO> userIdToUserVO = userIds.stream()
                .map(userId -> userService.getById(userId)) // 查询用户信息
                .filter(ObjUtil::isNotEmpty) // 过滤不存在的用户
                .map(UserVO::toVO) // 转换为UserVO
                .collect(Collectors.toMap(UserVO::getId, userVO -> userVO));

        // 2. 先将所有互动转换为Vo，并处理基础字段（用户信息、时间格式化）
        List<InteractionsVO> allVoList = interactionsList.stream()
                .map(interaction -> {
                    InteractionsVO vo = InteractionsVO.toVO(interaction);
                    // 设置评论者信息（所有评论都需要，包括子回复）
                    UserVO userVO = userIdToUserVO.get(interaction.getUserId());
                    vo.setUserVO(userVO);
                    return vo;
                })
                .collect(Collectors.toList());

        // 3. 构建子回复映射：key=parentId，value=该parentId对应的所有子回复
        Map<Integer, List<InteractionsVO>> parentIdToChildren = new HashMap<>();
        for (InteractionsVO vo : allVoList) {
            Integer parentId = vo.getParentId();
            if (parentId != null && parentId != 0) { // 只处理子回复（parentId≠0）
                // 若map中没有该parentId的列表，则初始化
                parentIdToChildren.computeIfAbsent(parentId, k -> new ArrayList<>())
                        .add(vo); // 将当前vo加入父级对应的子回复列表
            }
        }

        // 4. 筛选主评论（parentId=0），并为每个主评论绑定子回复
        List<InteractionsVO> mainVoList = allVoList.stream()
                .filter(vo -> vo.getParentId() == null || vo.getParentId() == 0)
                .map(vo -> this.bindChildren(vo, parentIdToChildren)) // 递归绑定子回复
                .collect(Collectors.toList());

        return mainVoList;
    }

    // 新增递归方法
    private InteractionsVO bindChildren(InteractionsVO parentVo, Map<Integer, List<InteractionsVO>> parentIdToChildren) {
        List<InteractionsVO> children = parentIdToChildren.get(parentVo.getId());
        if (children != null && !children.isEmpty()) {
            // 为每个子回复绑定它的子回复（递归）
            List<InteractionsVO> childVos = children.stream()
                    .map(vo -> this.bindChildren(vo, parentIdToChildren))
                    .collect(Collectors.toList());
            parentVo.setChildren(childVos);
        } else {
            parentVo.setChildren(new ArrayList<>());
        }
        return parentVo;
    }
}





